using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Tool
{
    public enum EDIT_TYPE
    {
        EDIT_PAN = 0, EDIT_VOLUME, EDIT_DISTORTION, EDIT_REVERB,
        EDIT_TOTAL_ITEMS
    }

    public enum MOUSE_SELECT_STATE
    {
        MOUSE_NORMAL = 0, MOUSE_DOWN, MOUSE_SELECTED,
        MOUSE_SELECT_TOTAL_ITEMS
    }

    public class SoundEditor
    {
        private System.Windows.Forms.Form m_parent;
        int m_blockWidth = 4;
        MOUSE_SELECT_STATE m_mouseState;
        int m_selectedPointIndex;
        Point m_maybeNewPointPos;
        int m_pointIndexToDelete;

        int m_panSliderX;
        int m_volumeSliderX;

        public SoundEditor(System.Windows.Forms.Form parent)
        {
            m_parent = parent;

            m_mouseState = MOUSE_SELECT_STATE.MOUSE_NORMAL;
            m_selectedPointIndex = -1;
            m_pointIndexToDelete = -1;

            m_maybeNewPointPos.X = 100;
            m_maybeNewPointPos.Y = 100;

            m_panSliderX = 0;
            m_volumeSliderX = 0;
        }

        public void Draw(Graphics gfx)
        {
            if (((Editor)m_parent).EventTree.SelectedNode == null || ((Editor)m_parent).EventTree.SelectedNode.Level == 0)
            {
                gfx.Clear(Color.LightGray);
                return;
            }

              // Set the background color
            gfx.Clear(Color.White);

            ArrayList sounds = ((Editor)m_parent).GetEventTree().GetEventSounds(((Editor)m_parent).EventTree.SelectedNode.Parent.Text);
            if (sounds.Count == 0)
            {
                return;
            }

            int index = ((Editor)m_parent).EventTree.SelectedNode.Index;
            EventSound sound = ((Editor)m_parent).GetEventTree().GetEventSound(((Editor)m_parent).EventTree.SelectedNode.Parent.Text, index);
            ArrayList volumePoints = sound.GetVolumeModifier().GetEditPoints();
            ArrayList panPoints = sound.GetPanModifier().GetEditPoints();
            ArrayList distPoints = sound.GetDistModifier().GetEditPoints();
            ArrayList reverbPoints = sound.GetReverbModifier().GetEditPoints();

            Color volumeActiveColor = sound.GetVolumeModifier().GetActiveColor();
            Color volumeInactiveColor = sound.GetVolumeModifier().GetInactiveColor();
            Color panActiveColor = sound.GetPanModifier().GetActiveColor();
            Color panInactiveColor = sound.GetPanModifier().GetInactiveColor();

            Color distActiveColor = sound.GetDistModifier().GetActiveColor();
            Color distInactiveColor = sound.GetDistModifier().GetInactiveColor();
            Color reverbActiveColor = sound.GetReverbModifier().GetActiveColor();
            Color reverbInactiveColor = sound.GetReverbModifier().GetInactiveColor();

            switch (((Editor)m_parent).SoundEditTypeComboBox.SelectedIndex)
            {
                case 0: // Pan
                    {
                        DrawLinesAndPoints(gfx, volumePoints, volumeInactiveColor, false, EDIT_TYPE.EDIT_VOLUME, sound);
                        DrawLinesAndPoints(gfx, distPoints, distInactiveColor, false, EDIT_TYPE.EDIT_DISTORTION, sound);
                        DrawLinesAndPoints(gfx, reverbPoints, reverbInactiveColor, false, EDIT_TYPE.EDIT_REVERB, sound);
                        DrawLinesAndPoints(gfx, panPoints, panActiveColor, true, EDIT_TYPE.EDIT_PAN, sound);
                        break;
                    }
                case 1: // Volume
                    {
                        DrawLinesAndPoints(gfx, distPoints, distInactiveColor, false, EDIT_TYPE.EDIT_DISTORTION, sound);
                        DrawLinesAndPoints(gfx, reverbPoints, reverbInactiveColor, false, EDIT_TYPE.EDIT_REVERB, sound);
                        DrawLinesAndPoints(gfx, panPoints, panInactiveColor, false, EDIT_TYPE.EDIT_PAN, sound);
                        DrawLinesAndPoints(gfx, volumePoints, volumeActiveColor, true, EDIT_TYPE.EDIT_VOLUME, sound);
                        break;
                    }
                case 2: // Distortion
                    {
                        DrawLinesAndPoints(gfx, reverbPoints, reverbInactiveColor, false, EDIT_TYPE.EDIT_REVERB, sound);
                        DrawLinesAndPoints(gfx, panPoints, panInactiveColor, false, EDIT_TYPE.EDIT_PAN, sound);
                        DrawLinesAndPoints(gfx, volumePoints, volumeInactiveColor, false, EDIT_TYPE.EDIT_VOLUME, sound);
                        DrawLinesAndPoints(gfx, distPoints, distActiveColor, true, EDIT_TYPE.EDIT_DISTORTION, sound);
                        break;
                    }
                case 3: // Reverb
                    {
                        DrawLinesAndPoints(gfx, panPoints, panInactiveColor, false, EDIT_TYPE.EDIT_PAN, sound);
                        DrawLinesAndPoints(gfx, volumePoints, volumeInactiveColor, false, EDIT_TYPE.EDIT_VOLUME, sound);
                        DrawLinesAndPoints(gfx, distPoints, distInactiveColor, false, EDIT_TYPE.EDIT_DISTORTION, sound);
                        DrawLinesAndPoints(gfx, reverbPoints, reverbActiveColor, true, EDIT_TYPE.EDIT_REVERB, sound);
                        break;
                    }
            }
        }

        private void DrawLinesAndPoints(Graphics gfx,ArrayList pointList,Color lineColor,bool drawPoints,EDIT_TYPE selectedType, EventSound sound)
        {
            Brush solidBrush = new SolidBrush(lineColor);

            // Create a pen to draw the line with
            Pen pen = new Pen(lineColor);

            // Draw the first point
            if (pointList.Count > 0 && drawPoints)
            {
                SoundEditorPoint firstPoint = (SoundEditorPoint)pointList[0];
                gfx.FillRectangle(solidBrush, new Rectangle(firstPoint.m_x - m_blockWidth / 2, firstPoint.m_y - m_blockWidth / 2, m_blockWidth, m_blockWidth));
            }

            for (int i = 0; i < pointList.Count - 1; ++i)
            {
                SoundEditorPoint currPoint = (SoundEditorPoint)pointList[i];
                SoundEditorPoint nextPoint = (SoundEditorPoint)pointList[i + 1];

                gfx.DrawLine(pen, new Point(currPoint.m_x, currPoint.m_y), new Point(nextPoint.m_x, nextPoint.m_y));

                if (drawPoints)
                {
                    gfx.FillRectangle(solidBrush, new Rectangle(nextPoint.m_x - m_blockWidth / 2, nextPoint.m_y - m_blockWidth / 2, m_blockWidth, m_blockWidth));
                }
            }

                // Draw the value of the trackbar
            float trackbarValue = (float)((Editor)m_parent).SoundEditorTrackbar.Value / 1000.0f; // should be between 0 and 1000
            int xPosValueLine = (int)( trackbarValue * (float)(((Editor)m_parent).SoundEditorPanel.Width-5) );

            switch (selectedType)
            {
                case EDIT_TYPE.EDIT_PAN: // Pan
                    {
                        if (drawPoints == true)
                        {
                            m_panSliderX = xPosValueLine;

                            GlobalVariable var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_PAN));
                            int varValue = (int)(var.m_value * (float)(((Editor)m_parent).SoundEditorPanel.Width - 5));
                            gfx.DrawLine(pen, new Point(varValue, 0), new Point(varValue, ((Editor)m_parent).Height));
                        }
                        else
                        {
                            GlobalVariable var = new GlobalVariable("poopy");
                            if (selectedType == EDIT_TYPE.EDIT_VOLUME)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_VOLUME));
                            }
                            else if (selectedType == EDIT_TYPE.EDIT_DISTORTION)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_DISTORTION));
                            }
                            else if (selectedType == EDIT_TYPE.EDIT_REVERB)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_REVERB));
                            }
                            int varValue = (int)(var.m_value * (float)(((Editor)m_parent).SoundEditorPanel.Width - 5));
                            gfx.DrawLine(pen, new Point(varValue, 0), new Point(varValue, ((Editor)m_parent).Height));
                        }
                        break;
                    }
                case EDIT_TYPE.EDIT_VOLUME: // Volume
                    {
                        if (drawPoints == true)
                        {
                            m_volumeSliderX = xPosValueLine;

                            GlobalVariable var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_VOLUME));
                            int varValue = (int)(var.m_value * (float)(((Editor)m_parent).SoundEditorPanel.Width - 5));
                            gfx.DrawLine(pen, new Point(varValue, 0), new Point(varValue, ((Editor)m_parent).Height));
                        }
                        else
                        {
                            GlobalVariable var = new GlobalVariable("poopy");
                            if (selectedType == EDIT_TYPE.EDIT_PAN)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_PAN));
                            }
                            else if (selectedType == EDIT_TYPE.EDIT_DISTORTION)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_DISTORTION));
                            }
                            else if (selectedType == EDIT_TYPE.EDIT_REVERB)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_REVERB));
                            }
                            int varValue = (int)(var.m_value * (float)(((Editor)m_parent).SoundEditorPanel.Width - 5));
                            gfx.DrawLine(pen, new Point(varValue, 0), new Point(varValue, ((Editor)m_parent).Height));
                        }
                        break;
                    }
                case EDIT_TYPE.EDIT_DISTORTION: // Distortion
                    {
                        if (drawPoints == true)
                        {
                            m_volumeSliderX = xPosValueLine;

                            GlobalVariable var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_DISTORTION));
                            int varValue = (int)(var.m_value * (float)(((Editor)m_parent).SoundEditorPanel.Width - 5));
                            gfx.DrawLine(pen, new Point(varValue, 0), new Point(varValue, ((Editor)m_parent).Height));
                        }
                        else
                        {
                            GlobalVariable var = new GlobalVariable("poopy");
                            if (selectedType == EDIT_TYPE.EDIT_PAN)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_PAN));
                            }
                            else if (selectedType == EDIT_TYPE.EDIT_VOLUME)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_VOLUME));
                            }
                            else if (selectedType == EDIT_TYPE.EDIT_REVERB)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_REVERB));
                            }
                            int varValue = (int)(var.m_value * (float)(((Editor)m_parent).SoundEditorPanel.Width - 5));
                            gfx.DrawLine(pen, new Point(varValue, 0), new Point(varValue, ((Editor)m_parent).Height));
                        }
                        break;
                    }
                case EDIT_TYPE.EDIT_REVERB: // Reverb
                    {
                        if (drawPoints == true)
                        {
                            m_volumeSliderX = xPosValueLine;

                            GlobalVariable var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_REVERB));
                            int varValue = (int)(var.m_value * (float)(((Editor)m_parent).SoundEditorPanel.Width - 5));
                            gfx.DrawLine(pen, new Point(varValue, 0), new Point(varValue, ((Editor)m_parent).Height));
                        }
                        else
                        {
                            GlobalVariable var = new GlobalVariable("poopy");
                            if (selectedType == EDIT_TYPE.EDIT_PAN)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_PAN));
                            }
                            else if (selectedType == EDIT_TYPE.EDIT_VOLUME)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_VOLUME));
                            }
                            else if (selectedType == EDIT_TYPE.EDIT_DISTORTION)
                            {
                                var = ((Editor)m_parent).m_globalVarManager.GetGlobalVariable(sound.GetVarName(EDIT_TYPE.EDIT_DISTORTION));
                            }
                            int varValue = (int)(var.m_value * (float)(((Editor)m_parent).SoundEditorPanel.Width - 5));
                            gfx.DrawLine(pen, new Point(varValue, 0), new Point(varValue, ((Editor)m_parent).Height));
                        }
                        break;
                    }
            }

            if (((Editor)m_parent).GetEventTree().GetEventSound((((Editor)m_parent).EventTree).SelectedNode.Parent.Text, (((Editor)m_parent).EventTree).SelectedNode.Index).IsUsingVar(EDIT_TYPE.EDIT_PAN) ||
                ((Editor)m_parent).GetEventTree().GetEventSound((((Editor)m_parent).EventTree).SelectedNode.Parent.Text, (((Editor)m_parent).EventTree).SelectedNode.Index).IsUsingVar(EDIT_TYPE.EDIT_VOLUME))
            {
                ((Editor)m_parent).SoundGlobalVarComboBox.Enabled = true;
            }
            else
            {
                ((Editor)m_parent).SoundGlobalVarComboBox.Enabled = false;
            }
        }

        public float GetVolumeSliderValue()
        {
            return (float)(m_volumeSliderX) / (float)(((Editor)m_parent).SoundEditorPanel.Width - 4);
        }

        public float GetPanSliderValue()
        {
            return (float)(m_panSliderX) / (float)(((Editor)m_parent).SoundEditorPanel.Width - 4);
        }

        public void MouseDown(int x, int y)
        {
            if (((Editor)m_parent).EventTree.SelectedNode == null || ((Editor)m_parent).EventTree.SelectedNode.Level == 0)
            {
                return;
            }

                // If nothing is yet selected
            if (m_mouseState == MOUSE_SELECT_STATE.MOUSE_NORMAL)
            {
                ArrayList pointList = GetPointListByComboBoxIndex(((Editor)m_parent).SoundEditTypeComboBox.SelectedIndex);

                for (int i = 0; i < pointList.Count; ++i)
                {
                        // Distance Check w/ radius 6
                    if( ( ((SoundEditorPoint)pointList[i]).m_x - x ) * ( ((SoundEditorPoint)pointList[i]).m_x - x ) +
                        ( ((SoundEditorPoint)pointList[i]).m_y - y ) * ( ((SoundEditorPoint)pointList[i]).m_y - y ) < 6 * 6 )
                    {
                        m_mouseState = MOUSE_SELECT_STATE.MOUSE_SELECTED;
                        m_selectedPointIndex = i;

                        SetMousePosition(pointList, m_selectedPointIndex, x, y);
                    }
                }
            }
                // If we are currently selecting something
            else if( m_mouseState == MOUSE_SELECT_STATE.MOUSE_SELECTED)
            {
                ArrayList pointList = GetPointListByComboBoxIndex(((Editor)m_parent).SoundEditTypeComboBox.SelectedIndex);

                SetMousePosition(pointList, m_selectedPointIndex, x, y);
            }
        }

        public void MouseUp()
        {
            m_mouseState = MOUSE_SELECT_STATE.MOUSE_NORMAL;
            m_selectedPointIndex = -1;
        }

        private void SetMousePosition(ArrayList pointList, int index,int x, int y)
        {
                // Make sure the Y positions do not move left or right if it is the first or last point
            if (m_selectedPointIndex != 0 && m_selectedPointIndex != pointList.Count - 1)
            {
                ((SoundEditorPoint)pointList[m_selectedPointIndex]).m_x = x;

                // Keep X within 0 and width
                if (((SoundEditorPoint)pointList[m_selectedPointIndex]).m_x < 0)
                {
                    ((SoundEditorPoint)pointList[m_selectedPointIndex]).SetX(0);
                }
                if (((SoundEditorPoint)pointList[m_selectedPointIndex]).m_x > ((SoundEditorPoint)pointList[m_selectedPointIndex]).m_width)
                {
                    ((SoundEditorPoint)pointList[m_selectedPointIndex]).SetX(((SoundEditorPoint)pointList[m_selectedPointIndex]).m_width);
                }

                // Keep X within its neighbors
                if (((SoundEditorPoint)pointList[m_selectedPointIndex]).m_x <= ((SoundEditorPoint)pointList[m_selectedPointIndex - 1]).m_x)
                {
                    ((SoundEditorPoint)pointList[m_selectedPointIndex]).SetX(((SoundEditorPoint)pointList[m_selectedPointIndex - 1]).m_x);
                }
                if (((SoundEditorPoint)pointList[m_selectedPointIndex]).m_x >= ((SoundEditorPoint)pointList[m_selectedPointIndex + 1]).m_x)
                {
                    ((SoundEditorPoint)pointList[m_selectedPointIndex]).SetX(((SoundEditorPoint)pointList[m_selectedPointIndex + 1]).m_x);
                }
            }

            ((SoundEditorPoint)pointList[m_selectedPointIndex]).m_y = y;

                // Keep Y within 0 and height
            if (((SoundEditorPoint)pointList[m_selectedPointIndex]).m_y < 0)
            {
                ((SoundEditorPoint)pointList[m_selectedPointIndex]).SetY(0);
            }
            if (((SoundEditorPoint)pointList[m_selectedPointIndex]).m_y > ((SoundEditorPoint)pointList[m_selectedPointIndex]).m_height)
            {
                ((SoundEditorPoint)pointList[m_selectedPointIndex]).SetY(((SoundEditorPoint)pointList[m_selectedPointIndex]).m_height);
            }
        }

        public void RightClick(int x, int y)
        {
            if (((Editor)m_parent).EventTree.SelectedNode == null || ((Editor)m_parent).EventTree.SelectedNode.Level == 0)
            {
                return;
            }

            ArrayList pointList = GetPointListByComboBoxIndex(((Editor)m_parent).SoundEditTypeComboBox.SelectedIndex);

            for (int i = 0; i < pointList.Count; ++i)
            {
                // Distance Check w/ radius 8
                if ((((SoundEditorPoint)pointList[i]).m_x - x) * (((SoundEditorPoint)pointList[i]).m_x - x) +
                    (((SoundEditorPoint)pointList[i]).m_y - y) * (((SoundEditorPoint)pointList[i]).m_y - y) < 8 * 8)
                {
                    if (i != 0 && i != pointList.Count - 1)
                    {
                        m_pointIndexToDelete = i;
                        ((Editor)m_parent).SoundEditorPanel.ContextMenuStrip = ((Editor)m_parent).SoundEditorRightClickMenuDelete;
                        return;
                    }
                }
            }

                // If we made it here, no point was found to delete
            ((Editor)m_parent).SoundEditorPanel.ContextMenuStrip = ((Editor)m_parent).SoundEditorRightClickMenuAdd;

            m_maybeNewPointPos.X = x;
            m_maybeNewPointPos.Y = y;
        }


        public void AddPoint()
        {
            ArrayList pointList = GetPointListByComboBoxIndex(((Editor)m_parent).SoundEditTypeComboBox.SelectedIndex);

            for (int i = 0; i < pointList.Count - 1; ++i)
            {
                if (m_maybeNewPointPos.X < ((SoundEditorPoint)pointList[i+1]).m_x &&
                    m_maybeNewPointPos.X > ((SoundEditorPoint)pointList[i]).m_x)
                {
                    pointList.Insert(i+1, new SoundEditorPoint(m_maybeNewPointPos.X, m_maybeNewPointPos.Y,
                                                             ((SoundEditorPoint)pointList[i + 1]).m_width,
                                                             ((SoundEditorPoint)pointList[i + 1]).m_height));
                    break;
                }
            }

            ((Editor)m_parent).SoundEditorPanel.Invalidate();
            ((Editor)m_parent).EventTimePanel.Invalidate();
        }

        public void DeletePoint()
        {
            ArrayList pointList = GetPointListByComboBoxIndex(((Editor)m_parent).SoundEditTypeComboBox.SelectedIndex);

            pointList.RemoveAt(m_pointIndexToDelete);

            ((Editor)m_parent).SoundEditorPanel.Invalidate();
            ((Editor)m_parent).EventTimePanel.Invalidate();
        }

        public ArrayList GetPointListByComboBoxIndex( int index )
        {
            int i = ((Editor)m_parent).EventTree.SelectedNode.Index;
            EventSound sound = ((Editor)m_parent).GetEventTree().GetEventSound(((Editor)m_parent).EventTree.SelectedNode.Parent.Text, i);

            switch (index)
            {
                case (int)(EDIT_TYPE.EDIT_PAN): // Pan
                    {
                        return sound.GetPanModifier().GetEditPoints();
                    }
                case (int)(EDIT_TYPE.EDIT_VOLUME): // Volume
                    {
                        return sound.GetVolumeModifier().GetEditPoints();
                    }

                case (int)(EDIT_TYPE.EDIT_DISTORTION): // Distortion
                    {
                        return sound.GetDistModifier().GetEditPoints();
                    }

                case (int)(EDIT_TYPE.EDIT_REVERB): // Reverb
                    {
                        return sound.GetReverbModifier().GetEditPoints();
                    }
                default:
                    {
                        return new ArrayList();
                    }
            }
        }
    }
}
